# -*- coding: utf-8 -*-
import random


def p(str):
    print(str)


class Resource(object):
    def __init__(self):
        super().__init__()
        self.width = 0
        self.height = 0
        # self.bitmap = ??


class Level(object):
    def __init__(self):
        super().__init__()
        self.snakes = []
        self.size = (26, 26)
        self.BLOCK_WIDTH = 20
        self.display_size = (
            (self.size[0]+2) * self.BLOCK_WIDTH, (self.size[1]+2) * self.BLOCK_WIDTH)
        self.food_list = []
        self.title = "蛇蛇崛起"
        self.game_end = False
        self.score = 0
        self.sfx_time_counter = 0
        self.reset()

    def reset(self):
        self.sfx_time_counter = 0
        self.game_end = False
        self.score = 0
        self.snakes = []
        self.food_list = []
        # level init
        self.FOOD_MAX_NUM = 3
        for i in range(0, self.FOOD_MAX_NUM):
            self.food_list.append(Food())
        # single snake
        self.snakes.append(Snake())
        pass


class Food(object):
    def __init__(self):
        super().__init__()
        # 0 - 普通 l + 3
        # 1 - 鸡腿 l + 3
        # 2 - 蘑菇 l - 1 / 麻痹
        self.type = 0
        # loc
        self.location = [0, 0]
        self.new_location = [0, 0]
        self.been_eated = True
        self.delay_tick = int(random.random() * 180)

    def almost_change(self):
        return self.delay_tick < 120

    def __set_delay(self):
        self.delay_tick = int(random.random() * 230) + 120

    def update_at_60fps(self, x_range: int, y_range: int):
        # move animation
        if self.location[0] != self.new_location[0]:
            if self.new_location[0] > self.location[0]:
                self.location[0] += 1
            else:
                self.location[0] -= 1
        if self.location[1] != self.new_location[1]:
            if self.new_location[1] > self.location[1]:
                self.location[1] += 1
            else:
                self.location[1] -= 1
        if self.delay_tick > 0:
            self.delay_tick -= 1
            return False
        self.__set_delay()
        self.new_location = (int(random.random() * x_range),
                             int(random.random() * y_range))
        self.type = int(random.random() * 3)
        if self.type >= 3:
            self.type = 2
        self.been_eated = False
        p('Food update %d (%d,%d)' %
          (self.type, self.new_location[0], self.new_location[1]))
        return True


class Snake(object):
    def __init__(self):
        super().__init__()
        self.old_direction = ''
        self.direction = 'D'
        self.l = 10
        self.speed = 16
        self.cut_body = 0
        self.body = []
        for i in range(self.l-1, -1, -1):
            #x y direction turn
            self.body.append((2, 0,self.direction,''))
        self.delay_tick = 0
        self.TICK_COUNT = 0
        self.sfx_speed_change_counter = 0
        self.sfx_mabi_counter = 0
        self.just_updated_direction = False

    def update_direction(self, dir):
        if self.just_updated_direction:
            return
        if dir == self.direction:
            return
        if 'L' == dir and self.direction == 'R':
            return
        if 'R' == dir and self.direction == 'L':
            return
        if 'U' == dir and self.direction == 'D':
            return
        if 'D' == dir and self.direction == 'U':
            return
        if 'L' == dir and self.body[0][0]-1 == self.body[1][0]:
            return
        if 'R' == dir and self.body[0][0]-+1 == self.body[1][0]:
            return
        if 'U' == dir and self.body[0][1]-1 == self.body[1][1]:
            return
        if 'D' == dir and self.body[0][1]+1 == self.body[1][1]:
            return
        self.old_direction  = self.direction
        self.direction = dir
        self.just_updated_direction = True
        p('Direction updated > %s'%self.direction)

    def is_hit_fence(self, xs, xt, ys, yt):
        if self.body[0][0] >= xs and self.body[0][0] <= xt:
            if self.body[0][1] >= ys and self.body[0][1] <= yt:
                return False
        return True

    def if_bite_self(self):
        for i in range(1, self.l):
            if self.if_bited(self.body[i][0], self.body[i][1]):
                if i == self.l - 1:
                    return -1
                return i
        return -1

    def if_bited(self, x, y):
        return (self.body[0][0] == x and self.body[0][1] == y)

    def set_to_l(self, l):
         # minimal
        if l < 5:
            l = 5
        if l > self.l:
            lv = self.body[self.l-1]
            num = l - self.l
            for i in range(0, num):
                self.body.append(lv)
            self.l = l
        if l < self.l:
            self.body = self.body[:l]
            self.l = l

    def update_at_60fps(self):
        if self.delay_tick < self.TICK_COUNT:
            self.delay_tick += 1
            return False

        self.TICK_COUNT = int(60 / self.speed)
        self.delay_tick = 0

        if self.sfx_speed_change_counter > 0:
            self.sfx_speed_change_counter -= 1
            if 0 == self.sfx_speed_change_counter:
                self.speed = 16

        if self.sfx_mabi_counter > 0:
            self.sfx_mabi_counter -= 1

        turn_dir = 'T_0'
        if self.just_updated_direction:
            if self.old_direction == 'L':
                if self.direction == 'U':
                    turn_dir = 'T_90'
                if self.direction == 'D':
                    turn_dir = 'T_0'
            if self.old_direction == 'U':
                if self.direction == 'L':
                    turn_dir = 'T_270'
                if self.direction == 'R':
                    turn_dir = 'T_0'
            if self.old_direction == 'R':
                if self.direction == 'U':
                    turn_dir = 'T_180'
                if self.direction == 'D':
                    turn_dir = 'T_270'
            if self.old_direction == 'D':
                if self.direction == 'L':
                    turn_dir = 'T_180'
                if self.direction == 'R':
                    turn_dir = 'T_90'

        for i in range(self.l-1, 0, -1):
            if self.just_updated_direction and i == 1:
                ov = self.body[i-1]
                self.body[i] = (ov[0],ov[1],ov[2],turn_dir)
            else:
                self.body[i] = self.body[i-1]

        self.just_updated_direction = False

        ins_dir = self.direction
        ov = self.body[0]
        if self.direction == 'L':
            nv = (ov[0]-1, ov[1],ins_dir,'')
        elif self.direction == 'R':
            nv = (ov[0]+1, ov[1],ins_dir,'')
        elif self.direction == 'U':
            nv = (ov[0], ov[1]-1,ins_dir,'')
        elif self.direction == 'D':
            nv = (ov[0], ov[1]+1,ins_dir,'')
        else:
            p('Assert ! Error at snake direction')
            self.direction = 'R'
        self.body[0] = nv
        return True


class Logic(object):
    def __init__(self, scen: Level):
        super().__init__()
        self.level = scen

    def update_player_control(self, player: int, direction: str):
        if player >= 0 and player < len(self.level.snakes):
            self.level.snakes[player].update_direction(direction)
            pass

    def handle_fx_result(self):
        # 结算特殊效果
        if self.level.sfx_time_counter > 0:
            self.level.sfx_time_counter -= 1
            if 0 == self.level.sfx_time_counter:
                for snake in self.level.snakes:
                    if snake.cut_body > 0:
                        p('snake body %d > %d' % (snake.l, snake.cut_body))
                        self.level.score -= (snake.l - snake.cut_body)
                        snake.set_to_l(snake.cut_body)
                        snake.cut_body = 0
            return True
        return False

    def update(self):
        if self.handle_fx_result():
            return

        snake_position_updated = False
        for snake in self.level.snakes:
            snake_position_updated = snake.update_at_60fps()
        for food in self.level.food_list:
            food.update_at_60fps(self.level.size[0], self.level.size[1])
        if snake_position_updated:
            for snake in self.level.snakes:
                # 撞到边缘
                if snake.is_hit_fence(0, self.level.size[0]-1, 0, self.level.size[1]-1):
                    self.level.game_end = True
                    return
                # 咬到自己
                b_cut = snake.if_bite_self()
                if b_cut > 0:
                    p('byte self at %d' % b_cut)
                    snake.cut_body = b_cut
                    self.level.sfx_time_counter = 120
                # 吃到食物
                for food in self.level.food_list:
                    if (not food.been_eated) and snake.if_bited(food.location[0], food.location[1]):
                        food.been_eated = True
                        # 普通
                        if food.type == 0:
                            snake.set_to_l(snake.l+1)
                            self.level.score += 1
                        # 加速
                        if food.type == 1:
                            snake.set_to_l(snake.l+3)
                            snake.speed = 32
                            snake.sfx_speed_change_counter = 100
                            self.level.score += 3
                        # 麻痹
                        if food.type == 2:
                            snake.set_to_l(snake.l-2)
                            snake.speed = 8
                            snake.sfx_speed_change_counter = 60
                            snake.sfx_mabi_counter = 60
                            self.level.score -= 2
